/*******************************I.MAX6UL启动文件,完成C环境初始化*****************************************/

// MRC/MCR p15,<opc1>,<Rt>,<CRn>,<CRm>,<opc2>

.global _start_                /*全局标号*/


/*描述: _start函数,首先是中断向量表的创建*/
_start:         //label  程序从此函数开始执行,此函数主要是设置C运行环境   相当于一个_start函数
    ldr pc,=Reset_Handler          //复位中断  注意这里的 =Reset_Handler
    ldr pc,=Undefined_Handler      //当指定的中断发生时,就会到这里执行指定的代码   应该是当单片机复位后会首先将中断向量表的地址加载到PC中
    ldr pc,=SVC_Handler            //这里表示软中断
    ldr pc,=PrefAbort_Handler
    ldr pc,=DataAbort_Handler
    .word 0                         /*RESERVED 分配32bit的内存空间,并且将其初始化为0*/
    ldr pc,=IRQ_Handler             //除了这个中断以外,其它的中断发生时都会进入死循环
    ldr pc,=FIQ_Handler             //这里的ldr pc,=Reset_Handler一旦执行,就会进入到Reset_Handler函数中  该函数执行完毕,就会进入到main函数中

/*复位中断  标号相当于一个函数*/
Reset_Handler:
    cpsid i                     //关闭全局中断   注意只是关闭了IRQ中断

    /*关闭I,Dcache和MMU 采取读-改-写 的方式*/
    mrc p15,0,r0,c1,c0,0       //将SCTLR中的值存到r0中
    bic r0,r0,#(0X1 << 12)     //    bic指令表示位清除  BIC Rd,Rn,#immed  ( 前面的表示 Rd=Rn & (~#immed) )
    bic r0,r0,#(0X1 << 2)      //上面表示清除C1的I位,关闭I Cache    这里表示清除C1的C位,关闭D Cache
    bic r0,r0,#0X2             //清除C1的A位,关闭对齐检查     立即数表示有两种方式  1.以#开头  2.以=开头
    bic r0,r0,#(0X1 << 11)     //清除C1的Z位,关闭分支预测
    bic r0,r0,#0X1             //清除C1的M位,关闭MMU
    mcr p15,0,r0,c1,c0,0       //将r0中的值存到SCTLR中

#if 0
    /*汇编版本设置中断向量表偏移*/
    ldr r0,=0X87800000
    dsb                       //数据同步隔离,仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令
    isb                       //指令同步隔离,最严格,他会清洗流水线,保证所有他前面的指令都执行完毕之后,才执行它后面的指令
    mcr p15,0,r0,c12,c0,0     //  将r0中的值(0X87800000)放入VBAR寄存器中
    dsb
    isb    //这里的dsb和isb不知道是干嘛的
#endif

    /*设置各个模式下的栈指针  除了User和Sys模式下是共用同一个sp外,其它模式下的sp都是单独的 不同模式下的sp其物理寄存器其实是不同的
      注意:IMX6UL的堆栈是向下增长的   RT-thread也是的
      堆栈指针地址一定要是4字节地址对齐的!!!
      DDR范围: 0X80000000 ~ 0X9FFFFFFF  或者 0X8FFFFFFF
      */
    /*进入IRQ模式  一般中断模式 */
    mrs r0,cpsr                 //  mrs是专门用来操作cpsr寄存器的指令
    bic r0,r0,#0X1F             //  将r0的低5位清零
    orr r0,r0,#0X12             //  r0=r0 | #immed   这里表示将r0的低5位设置为0X12   表示使用IRQ模式
    msr cpsr,r0                 //  将r0的数据写入到cpsr中
    ldr sp,=0x80600000          //IRQ模式的栈首地址为0X80600000,大小为2MB    就是从0X80600000到0X80400000

    /*进入SYS模式  系统模式,用于运行特权级的操作系统任务*/
    mrs r0,cpsr                 //  mrs是专门用来操作cpsr寄存器的指令
    bic r0,r0,#0X1F             //  将r0的低5位清零
    orr r0,r0,#0X1F             //  r0=r0 | #immed   这里表示将r0的低5位设置为0X12   表示使用SYS模式
    msr cpsr,r0                 //  将r0的数据写入到cpsr中
    ldr sp,=0x80400000          //SYS模式的栈首地址为0X80400000,大小为2MB    就是从0X80400000到0X80200000

    /*进入SVC模式  超级管理员模式,特权模式,仅供操作系统使用*/
    mrs r0,cpsr                 //  mrs是专门用来操作cpsr寄存器的指令
    bic r0,r0,#0X1F             //  将r0的低5位清零
    orr r0,r0,#0X13             //  r0=r0 | #immed   这里表示将r0的低5位设置为0X12   表示使用SVC模式
    msr cpsr,r0                 //  将r0的数据写入到cpsr中
    ldr sp,=0x80200000          //SVC模式的栈首地址为0X80200000,大小为2MB    就是从0X80200000到0X80000000

    cpsie i                     //打开全局中断   就是开启IRQ中断

#if 0
    /*使能IRQ中断*/
    mrs r0,cpsr                 //读取cpsr寄存器值到r0中
    bic r0,r0,#0X80             //将r0中的bit7清零,也就是CPSR中的I位清零,表示允许IRQ中断
    msr cpsr,r0                 //将r0写入到cpsr中
#endif

    b main                      //调转到main函数  不会返回

/*未定义中断*/
Undefined_Handler:
    ldr r0,=Undefined_Handler
    bx r0           //这里其实表示跳转到Undefined_Handler处

/*SVC中断*/
SVC_Handler:
    ldr r0,=SVC_Handler
    bx r0

/*预取终止中断*/
PrefAbort_Handler:
    ldr r0,=PrefAbort_Handler
    bx r0

/*数据终止中断*/
DataAbort_Handler:
    ldr r0,=DataAbort_Handler
    bx r0

/*IRQ中断!重点!!!!!  表示当前处于IRQ中断模式*/
IRQ_Handler:
    push {lr}                       //保存lr地址  这里的
    push {r0-r3,r12}                //保存r0-r3,r12寄存器   其实就是将r0,r1,r2,r3,r12压入堆栈中  

    mrs r0,spsr    //读取spsr寄存器    mrs和msr  spsr和cpsr  其中SPSR叫做备份程序状态寄存器  
                   //当特定的异常发生时,SPSR寄存器用来保存当前程序状态寄存器(CPSR)的值.当异常退出以后就可以用SPSR中保存的值来恢复CPSR (自动操作)

    push {r0}      //保存spsr寄存器   其实是将进入IRQ_Handler之前的CPSR的值保存下来
    /*上面是中断现场保护*/


    mrc p15,4,r1,c15,c0,0           // MRC/MCR p15,<opc1>,<Rt>,<CRn>,<CRm>,<opc2>    获取GIC的基地址,并将GIC的基地址的值放入r1中
    add r1,r1,#0X2000               //GIC基地址加0X2000 ,得到CPU接口基地址
    ldr r0,[r1,#0XC]                //CPU接口基地址加0X0C就是GICC_IAR寄存器,GICC_IAR保存着当前发生中断的中断号,我们要根据这个中断号来绝对调用
                                    //哪个中断服务函数
    /*上面是获取中断号*/ 
    /*在汇编调用C函数的时候建议形参不要超过4个,形参可以由r0~r3这四个寄存器来传递,如果形参大于4个,那么大于4个的部分要使用堆栈进行传递*/
    push {r0,r1}                    //这里实际上是将 CPU的接口基地 和 GICC_IAR中的值 压入堆栈中  这个是起到保护作用吗??
    cps  #0X13                      //cps指令:在除了用户模式的其它模式下,对处理器运行模式进行修改     进入SVC模式,允许其他中断再次进去

    push {lr}                       //保存SVC模式的lr寄存器      几个模式都有其特定的堆栈  这里应该是保护lr中得值  因为下面blx指令会影响lr中的值
    ldr r2,=system_irqhandler       //加载C语言中断处理函数到r2寄存器中
    blx r2                          //进入C语言中断处理函数,带有一个参数  是哪个参数???  调用blx指令时会将blx指令的下一条指令的地址保存在lr中
    pop {lr}                        //执行C语言中断服务函数,lr出栈

    cps #0X12                       //进入IRQ模式
    pop {r0,r1}                     //  

    str r0,[r1,#0X10]               //r0中存的就是GICC_IAR寄存器中的值  r1中存的就是CPU接口基地址的值  将C_IAR的值写入C_EOIR中  表示中断执行完成 写EOIR

    pop {r0}                        //将IRQ中断前spsr的值放到r0中
    msr  spsr_cxsf,r0               //  spsr_cxsf表示spsr_all   c:xPSR[7:0]  x:xPSR[15:8]  s:xPSR[23:16]  f:xPSR[31:24] 
                                    //  当异常退出时,就可以用SPSR中保存的值来恢复CPSR (自动操作)

    pop {r0-r3,r12}                 //r0-r3,r12出栈
    pop {lr}                        //lr出栈  pop{pc}时,就表示将堆栈中的值直接赋给了lr
    subs pc,lr,#4                   // subs中的s表示把进位结果写入CPSR中  SUB Rd,Rn,#immed 表示  Rd=Rn-#immed  这里表示  pc=lr-4
    

/*FIQ中断*/
FIQ_Handler:
    ldr r0,=FIQ_Handler
    bx r0


    /*进入SVC模式 进入IRQ模式*/
    